#pragma once

#include <iostream>
#include <memory>
#include <unistd.h>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Log.hpp"

using namespace SocketModule;
using namespace LogModule;

// 暂时不做过多解耦
class EpollServer
{
    const static int size = 64; // 最大事件数
    const static int defaultfd = -1;

public:
    EpollServer(int port) : _listensock(std::make_unique<TcpSocket>()), _isrunning(false), _epfd(defaultfd)
    {
        // 1. 创建监听套接字
        _listensock->BuildTcpSocketMethod(port); // 3
        // 2. 创建 epoll 模型
        _epfd = epoll_create(256);
        if (_epfd < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_create error!";
            exit(EPOLL_CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "epoll_create success, epfd: " << _epfd; // 4

        // 3. 注册监听套接字到 epoll 模型
        struct epoll_event ev;
        ev.events = EPOLLIN;            // 监听套接字的读事件
        ev.data.fd = _listensock->Fd(); // 监听套接字的 fd
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Fd(), &ev);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "epoll_ctl: add listen sockfd error!";
            exit(EPOLL_CTL_ERR);
        }
        LOG(LogLevel::INFO) << "epoll_ctl: add listen sockfd success, listen sockfd: " << _listensock->Fd(); // 5
    }

    ~EpollServer()
    {
        _listensock->Close();
        if (_epfd >= 0)
        {
            close(_epfd);
        }
    }

    void Start()
    {
        int timeout = -1; // 超时时间
        _isrunning = true;
        while (_isrunning)
        {
            // 1. epoll_wait 等待事件 --- 不能直接Accept！--- 从就绪队列中捞取就绪 fd
            int n = epoll_wait(_epfd, _revs, size, timeout);
            switch (n)
            {
            case 0: // 超时
                LOG(LogLevel::DEBUG) << "epoll_wait timeout!";
                break;
            case -1: // 出错
                LOG(LogLevel::FATAL) << "epoll_wait error!";
                break;
            default:
                // 事件派发
                Dispatcher(n);
                break;
            }
        }
        _isrunning = false;
    }

    void Stop()
    {
        _isrunning = false;
    }

private:
    // 连接管理器
    void Accepter() // 新连接到来处理
    {
        InetAddr client;
        int sockfd = _listensock->Accept(&client); // 这里的 Accept 就不会阻塞了！！！因为 listen 套接字已经就绪了！这就是把等过程和拷贝的过程分离了！
        if (sockfd >= 0)
        {
            // 获取新连接成功
            LOG(LogLevel::DEBUG) << "get a new link, sockfd: " << sockfd << ", client is: " << client.StringAddr();
            // 获取新连接到来成功，然后呢？可以直接进行 read/recv() 操作吗？

            struct epoll_event ev;
            ev.events = EPOLLIN; // 监听读事件
            ev.data.fd = sockfd; // 监听 sockfd
            int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "epoll_ctl: add sockfd error!";
            }
            else
            {
                LOG(LogLevel::INFO) << "epoll_ctl: add sockfd success, sockfd: " << sockfd;
            }
        }
    }

    // IO 处理器
    void Recver(int sockfd) // 普通fd收到数据的读事件处理
    {
        // 处理 sockfd 读事件
        // 我们在这里读取的时候，就不会阻塞了 --- 因为 select 已经完成等操作了！
        char buf[1024];
        ssize_t n = recv(sockfd, buf, sizeof(buf - 1), 0);
        // recv 读的时候会有BUG！因为无法保证能够收到一个完整的请求！--- TCP 是流式协议！
        // 我们目前先不做处理，等到 epoll 的时候，再做处理！
        if (n > 0)
        {
            buf[n] = 0;
            LOG(LogLevel::DEBUG) << "Client say#" << buf;
        }
        else if (n == 0)
        {
            // 客户端关闭连接
            LOG(LogLevel::DEBUG) << "Client close the link ......";
            // 从 epoll 中移除 fd 的关心 && 关闭 fd --- 细节：调用epoll_ctl 来移除，只能移除合法 fd --- 先移除，再关闭！
            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            if (m < 0)
            {
                LOG(LogLevel::FATAL) << "epoll_ctl: del sockfd error!";
            }
            else
            {
                LOG(LogLevel::INFO) << "epoll_ctl: del sockfd success, sockfd: " << sockfd;
            }
            close(sockfd);
        }
        else
        {
            // 读错误
            LOG(LogLevel::ERROR) << "recv error, sockfd: " << sockfd;
            // 从 epoll 中移除 fd 的关心 && 关闭 fd --- 细节：调用epoll_ctl 来移除，只能移除合法 fd --- 先移除，再关闭！
            int ret = epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            if (ret < 0)
            {
                LOG(LogLevel::FATAL) << "epoll_ctl: del sockfd error!";
            }
            else
            {
                LOG(LogLevel::INFO) << "epoll_ctl: del sockfd success, sockfd: " << sockfd;
            }
            close(sockfd); // 关闭连接
        }
    }

    // 事件派发器
    void Dispatcher(int rnum)
    {
        LOG(LogLevel::DEBUG) << "event ready ......"; // LT模式:水平触发模式
        for (int i = 0; i < rnum; i++)
        {
            // epoll 也要循环处理就绪事件 --- 这是应该的，本来就有可能有多个 fd 就绪！
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;
            if (revents & EPOLLIN) // 读事件就绪
            {
                // listen 套接字就绪 还是 普通套接字就绪了？
                if (sockfd == _listensock->Fd())
                {
                    // 新连接到来
                    Accepter();
                }
                else
                {
                    // 普通套接字就绪
                    // 找到对应 fd 位置
                    Recver(sockfd);
                }
            }

            // if(revents & EPOLLOUT)// 写事件就绪
            //{}
        }
    }

private:
    std::unique_ptr<Socket> _listensock;
    bool _isrunning;
    int _epfd;                      // epoll 模型
    struct epoll_event _revs[size]; // 事件数组
};